package com.woniuxy.house.config;
import static com.google.code.kaptcha.Constants.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import com.woniuxy.house.entity.ApiSecurityUser;
import com.woniuxy.house.filter.*;
import com.woniuxy.house.peopertiesentity.CaptchaProperties;
import com.woniuxy.house.peopertiesentity.JwtProperties;
import com.woniuxy.house.utils.JwtUtil;
import com.woniuxy.house.vo.ResponseEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Properties;

/**
 * @author baobo
 * @date 2022-08-29-11:20
 */
@Slf4j
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	public 	JwtUtil jwtUtil;

	@Autowired
	private UserDetailsService userDetailsService;//注入
	/**
	 * 配置获取用户账号密码的过滤器
	 * @return
	 */
	@Bean
	public ApiUsernamePasswordAuthenticationFilter apiUsernamePasswordAuthenticationFilter() throws Exception {
		//创建实例,在此实例中
		ApiUsernamePasswordAuthenticationFilter apiAuthenticationFilter = new ApiUsernamePasswordAuthenticationFilter("/api/user/login");
		//authenticationManager()来自WebSecurityConfigurer<WebSecurity>
		apiAuthenticationFilter.setAuthenticationManager(authenticationManager());
		//认证成功
		apiAuthenticationFilter.setAuthenticationSuccessHandler((req,resp,authentication)->{
			log.debug("+++++++++++++++++++++++++++++进入ApiAuthenticationFilter验证成功回调函数+-----------------------------------");
			//返回json数据
			PrintWriter writer = resp.getWriter();
			ObjectMapper objectMapper = new ObjectMapper();
			//查询
			ApiSecurityUser user = (ApiSecurityUser)authentication.getPrincipal();
			log.debug("id：{}user:{}",user.getId(),user);
			String jwt = jwtUtil.createJWT(user.getId(), user.getUsername());
			String rsp[]=new String[]{jwt,user.getId().toString()};
			String s= objectMapper.writeValueAsString(new ResponseEntity("200","成功",rsp));
			writer.write(s);
			writer.close();
		});
		//认证失败
		apiAuthenticationFilter.setAuthenticationFailureHandler((req,resp,authentication)->{
			log.debug("+++++++++++++++++++++++++++++进入ApiAuthenticationFilter验证失败回调函数+-----------------------------------");
			ObjectMapper objectMapper = new ObjectMapper();
			String s= objectMapper.writeValueAsString(new ResponseEntity("500","账号或密码错误",null));
			PrintWriter writer = resp.getWriter();
			writer.write(s);
			writer.close();
		});
		return apiAuthenticationFilter;
	}
	@Bean
	public ApiJwtAuthenticationFilter apiJwtAuthenticationFilter() throws Exception {//创建实例,在此实例中
		ApiJwtAuthenticationFilter apiJwtAuthenticationFilter = new ApiJwtAuthenticationFilter("/api/user/**");
		//authenticationManager()来自WebSecurityConfigurer<WebSecurity>
		apiJwtAuthenticationFilter.setAuthenticationManager(authenticationManager());
		//认证失败
		apiJwtAuthenticationFilter.setAuthenticationFailureHandler((req,resp,authentication)->{
			ObjectMapper objectMapper = new ObjectMapper();
			String s= objectMapper.writeValueAsString(new ResponseEntity("500","jwt认证失败",null));
			PrintWriter writer = resp.getWriter();
			writer.write(s);
			writer.close();
		});
		//成功
		apiJwtAuthenticationFilter.setAuthenticationSuccessHandler((req,resp,authentication)->{
			req.getRequestDispatcher(req.getRequestURI()).forward(req,resp);
		});
		return apiJwtAuthenticationFilter;
	}
	/**
	 * 由于抛出的账号不存在异常被DaoAuthenticationProvider封装成为了BadCredentialsException,
	 * 所以新建一个DaoAuthenticationProvider实例将里面的setHideUserNotFoundExceptions(false)
	 * 修改为不隐藏封装
	 * @return
	 */
	@Bean
	public DaoAuthenticationProvider daoAuthenticationProvider(){
		//
		DaoAuthenticationProvider daoAuthenticationProvider=new DaoAuthenticationProvider();
		//
		daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
		//
		daoAuthenticationProvider.setUserDetailsService(userDetailsService);
		//
		daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
		return daoAuthenticationProvider;
	}

	@Bean
	public JwtAuthenticationProvider jwtAuthenticationProvider() {
		return new JwtAuthenticationProvider();
	}
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		//将两个Provier增加到Security中
		auth.authenticationProvider(daoAuthenticationProvider()).authenticationProvider(jwtAuthenticationProvider());
	}
	@Bean
	public CaptchaFilter captchaFilter() {
		return new CaptchaFilter();
	}

	@Override
	public void configure(HttpSecurity http) throws Exception {

		http.addFilterBefore(apiUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
		//设置jwt过滤器位置
		http.addFilterAfter(apiJwtAuthenticationFilter(), ApiUsernamePasswordAuthenticationFilter.class);
		http.addFilterBefore(captchaFilter(),ApiUsernamePasswordAuthenticationFilter.class);
		//设置过滤过滤器位置
		http.cors().configurationSource(configurationSource());   //跨域配置
		http.antMatcher("/api/user/**")  //指定以/api/开头的请求使用以下配置
				.authorizeHttpRequests()//AuthorizationManagerRequestMatcherRegistry授权管理器请求匹配器注册表
				.antMatchers(HttpMethod.OPTIONS).permitAll()  //请求方法OPTIONS放行
				.antMatchers("/api/user/login","/api/regist","/api/sendphonemsg","/api/resetpassword").permitAll()
				.anyRequest().authenticated()//,除了上面方法都需要认证成功
				.and()
				.exceptionHandling()
				.accessDeniedHandler((req,resp,accessDeniedException)->{
					//处理访问拒绝异常
					PrintWriter writer = resp.getWriter();
					ObjectMapper mapper = new ObjectMapper();
					String s = mapper.writeValueAsString(ResponseEntity.NO_VALID);
					writer.write(s);
				})
				.and()
				//千万不要取消session生成，项目需要(修改思路后不需要session存储数据，采用redis存储数据)
				.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
				.and().csrf().disable();

	}
	/**
	 * 设置跨域问题请求头
	 * @return
	 */
	private CorsConfigurationSource configurationSource() {
		//其子类
		UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
		//具体配置对象
		CorsConfiguration corsConfiguration = new CorsConfiguration();
		//corsConfiguration.setAllowedOrigins();
		corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
		corsConfiguration.setAllowedMethods(Arrays.asList("*"));
		corsConfiguration.setExposedHeaders(Arrays.asList("*"));
		corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
		corsConfiguration.setAllowedOriginPatterns(Arrays.asList("*"));
		//加上了这一句，大致意思是可以携带 cookie
		//最终的结果是可以 在跨域请求的时候获取同一个 session
		corsConfiguration.setAllowCredentials(true);
		//将配置好的跨域配置对象注册到跨域配置源中
		urlBasedCorsConfigurationSource.registerCorsConfiguration("*", corsConfiguration);
		return urlBasedCorsConfigurationSource;
	}

	/**
	 * 配置密码加密算法
	 * @return
	 */
	@Bean
	public PasswordEncoder passwordEncoder(){

		return new BCryptPasswordEncoder();
	}
	/**
	 * 配置处理json
	 * @return
	 */
	@Bean
	public ObjectMapper objectMapper(){
		return new ObjectMapper();
	}

	@Bean
	public AuthenticationFailureHandler authenticationFailureHandler(){
		return new AuthenticationFailureHandler() {
			@Override
			public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
				PrintWriter writer = response.getWriter();
				ObjectMapper objectMapper = objectMapper();
				String s = objectMapper.writeValueAsString(new ResponseEntity("400", "验证码错误", null));
				writer.write(s);
				writer.close();
			}
		};
	}

	@Autowired
	private CaptchaProperties captchaProperties;

	@Bean
	public DefaultKaptcha defaultKaptcha() {
		Properties properties = new Properties();
		// 是否有边框 默认为true 我们可以自己设置yes，no
		properties.setProperty("kaptcha.border", "no");
		// 验证码文本字符颜色 默认为Color.BLACK
		properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, captchaProperties.getColor());
		// 验证码图片宽度 默认为200
		properties.setProperty(KAPTCHA_IMAGE_WIDTH, captchaProperties.getWidth());
		// 验证码图片高度 默认为50
		properties.setProperty(KAPTCHA_IMAGE_HEIGHT, captchaProperties.getHeight());
		// 验证码文本字符大小 默认为40
		properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, captchaProperties.getSize());
		// KAPTCHA_SESSION_KEY
		properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, captchaProperties.getKey());
		// 验证码文本字符长度 默认为5
		properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, captchaProperties.getLength());
		// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
		properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, captchaProperties.getNames());
		// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
		properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, captchaProperties.getImpl());
		Config config = new Config(properties);

		DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
		defaultKaptcha.setConfig(config);
		return defaultKaptcha;
	}
	    @Bean
    public Filter1 filter1() {
        return new Filter1();
    }
	@Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.addUrlPatterns("/*");
        bean.setFilter(filter1());
        bean.setOrder(Integer.MIN_VALUE);
        return bean;
    }

}
